﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace rFDevLib {
    // TGMDimensions Struct
    // Holds basic measurements and specifications for a tire
    public struct TGMDimensions {
        public double treadWidth, sidewallHeight, rimRadius, rimWidth,
            sidewallBulge, ringBulge, sidewallThickness, treadThickness,
            cornerLength, cornerRounding;
        public int latRes, radRes;

        public double GetCircumference() {
            return (rimRadius + sidewallHeight) * 2.0 * Math.PI;
        }
    }

    public class MaterialParams {
        public double temp = 273.15, // Reference temperature, Kelvin
            density, // kg/m^3
            ym, // Young's Modulus/Elasticity Modulus, Pa
            pr, // Poison's Ratio
            df, // Damping factor. TGM quickstart says this isn't implemented, and should be -1
            sh, // Specific heat, ?
            tc; // Thermal conductivity, ?

        public string ToString() {
            return temp.ToString() + "," +
                density.ToString() + "," +
                ym.ToString() + "," +
                pr.ToString() + "," +
                df.ToString() + "," +
                sh.ToString() + "," +
                tc.ToString();
        }
    } // class MaterialParams
    
    public class Material {
        //public string name;
        public List<MaterialParams> properties;

        public Material() {
            properties = new List<MaterialParams>();
        }

        public void WriteToTGMStream( string prefix, System.IO.StreamWriter str ) {
            for ( int i = 0; i <= properties.Count; i++ ) {
                str.WriteLine( prefix + "=(" + properties[i].ToString() + ")" );
            }
        }
    } // class Material

    public class Ply {
        double thickness, orientation;
        int connect;
        Material mat;

        public Ply( double _t = 0.0, double _o = 90.0, int _c = 0 ) {
            thickness = _t;
            orientation = _o;
            connect = _c;
        }
    }

    // Node class
    // (Vertex, Point) along surface of tire with properties
    public class Node {
        public double x, y, d; // X Pos, Y Pos, Depth
        public double rar1, rar2; // Ring and Rim params
        public double treadDepth;
        public string comment;

        public Material treadMat;
        public Material bulkMat;
        public List<Ply> plyLayers;

        public Node() {
            plyLayers = new List<Ply>();
        }

        public void WriteToStream( System.IO.StreamWriter w ) {
            w.WriteLine( "Geometry=(" + x.ToString() + "," + y.ToString() + "," + d.ToString() + ")" );
            //bulkMat.ToTGMText("bulkMaterial", w);
            w.WriteLine( "AnisoCarcassConductivityMult=(1,1,1)" );
            w.WriteLine( "TreadDepth=" + treadDepth.ToString() );
            //treadMat.ToTGMText("TreadMaterial", w);
            w.WriteLine( "RingAndRim=(" + rar1 + "," + rar2 + ")" );

            //foreach (Ply p in plyLayers)
            //{
            //    p.ToTGMText(w);
            //}
        }
    }

    public class TGMTire {
        private Node[] nodes = null;
        private TGMDimensions m_specs;
        private rFDataFile m_BaseData; // If loaded from existing TGM file, this will be filled in. (Fill in on save? Or before?)

        public List<double> m_refPres;
        public List<double> m_refSpeed;

        public TGMTire() {
        }

        public TGMTire( TGMDimensions specs ) {
            BuildFromSpecs( specs );
        }

        public void BuildFromSpecs( TGMDimensions specs ) {
            m_specs = specs;

            nodes = new Node[specs.latRes];

            for ( int i = 0; i < specs.latRes; i++ ) {
                nodes[i] = new Node();
                nodes[i].d = 0.05;
                nodes[i].treadDepth = 0.005;
            }

            int sidewallRes = Math.Max( (int)((double)specs.latRes * (specs.sidewallHeight / (specs.sidewallHeight + specs.sidewallHeight + specs.treadWidth))), 1 );
            int treadRes    = specs.latRes - sidewallRes - sidewallRes;

            nodes[0].comment = "Positive X Rim";
            nodes[sidewallRes].comment = "Positive X Sidewall/Tread corner";
            nodes[specs.latRes - sidewallRes - 1].comment = "Negative X Sidewall/Tread corner";
            nodes[specs.latRes - 1].comment = "Negative X Rim";

            double HalfRimWidth     = specs.rimWidth * 0.5;
            double HalfTreadWidth   = specs.treadWidth * 0.5;

            // Sidewall placement
            for ( int i = 0; i <= sidewallRes; i++ ) {
                double ratio        = (double)i / (double)sidewallRes;
                double bulgeShift   = (ratio - (ratio * ratio)) * 4.0 * specs.sidewallBulge;

                Node posNode = nodes[i];
                Node negNode = nodes[specs.latRes - i - 1];

                posNode.x = (HalfRimWidth * (1.0 - ratio)) + (HalfTreadWidth * ratio) + bulgeShift;
                negNode.x = (-HalfRimWidth * (1.0 - ratio)) - (HalfTreadWidth * ratio) - bulgeShift;

                posNode.y = negNode.y = -specs.rimRadius - (specs.sidewallHeight * ratio);

                if ( ratio >= 0.5 )
                    posNode.rar1 = negNode.rar1 = 1.0;

                posNode.d = negNode.d = specs.sidewallThickness;
                posNode.treadDepth = negNode.treadDepth = 0.0019;

                // Corner rounding
                if ( specs.cornerLength > 0.0 ) {
                    double edgeDist = -(-posNode.y - specs.rimRadius) + specs.sidewallHeight;

                    double edgeRatio = 1.0 - Math.Min( edgeDist / specs.cornerLength, 1.0 );
                    edgeRatio *= 0.5;
                    edgeRatio = edgeRatio * edgeRatio;

                    posNode.x -= edgeRatio * specs.cornerRounding * specs.cornerLength;
                    posNode.y += edgeRatio * specs.cornerRounding * specs.cornerLength;
                    negNode.x += edgeRatio * specs.cornerRounding * specs.cornerLength;
                    negNode.y += edgeRatio * specs.cornerRounding * specs.cornerLength;

                    double asf = 1.0;
                }

                // Add material parameters here when that's in!
            }

            // Tread placement
            for ( int i = sidewallRes; i < specs.latRes - sidewallRes; i++ ) {
                double ratio        = (double)(i - sidewallRes) / (double)(treadRes - 1);
                double bulgeShift   = (ratio - (ratio * ratio)) * 4.0 * specs.ringBulge;

                nodes[i].x = HalfTreadWidth - (specs.treadWidth * ratio);
                nodes[i].y = -specs.rimRadius - specs.sidewallHeight - bulgeShift;
                nodes[i].d = specs.treadThickness;

                nodes[i].rar1 = 1.0;
                nodes[i].treadDepth = 0.005;

                // Corner rounding
                if ( specs.cornerLength > 0.0 ) {
                    double edgeDist = nodes[i].x + HalfTreadWidth;
                    if ( nodes[i].x > 0.0 )
                        edgeDist = -nodes[i].x + HalfTreadWidth;

                    double edgeRatio = 1.0 - Math.Min( edgeDist / specs.cornerLength, 1.0 );
                    edgeRatio *= 0.5;
                    edgeRatio = edgeRatio * edgeRatio;

                    nodes[i].y += edgeRatio * specs.cornerRounding * specs.cornerLength;
                    if ( nodes[i].x > 0.0 ) {
                        nodes[i].x -= edgeRatio * specs.cornerRounding * specs.cornerLength;
                    } else {
                        nodes[i].x += edgeRatio * specs.cornerRounding * specs.cornerLength;
                    }
                }

                // Materials and ply information next!
            }

            nodes.First().rar2 = nodes.Last().rar2 = 1e9;
        }

        public void DrawGraphic( TGMGraphic gfx ) {
            foreach ( Node n in nodes ) {
                gfx.AddNode( n );
            }
        }

        public void WriteToStream( System.IO.StreamWriter str ) {
            str.WriteLine( "[QuasiStaticAnalysis]" );
            str.WriteLine( "NumLayers=2\nNumSections=" + m_specs.radRes.ToString() );
            str.WriteLine( "RimVolume=" + (m_specs.rimRadius * m_specs.rimRadius * Math.PI * m_specs.rimWidth).ToString() );
            str.WriteLine( "RealtimeCamberLimit=45" );
            //str.WriteLine( "GaugePressure=0\nGaugePressure=150000\nGaugePressure=300000" );
            foreach ( double a in m_refPres )
                str.WriteLine( "GaugePressure=" + a.ToString() );

            str.WriteLine( "CarcassTemperature=273.15\nCarcassTemperature=373.15" );
            //str.WriteLine( "RotationSquared=0\nRotationSquared=80000" );
            foreach ( double a in m_refSpeed )
                str.WriteLine( "RotationSquared=" + a.ToString() );

            str.WriteLine( "NumNodes=" + nodes.Length.ToString() );
            str.WriteLine( "VolumeLoad=0\nLoadCamber=0\nLoadInclination=0\nLoadDeflection=0\nTotalMass=0\nTotalInertiaStandard=(0,0,0)\nRingMass=0\nRingInertiaStandard=(0,0,0)" );
            str.WriteLine();

            for ( int i = 0; i < nodes.Length; i++ ) {
                Node n = nodes[i];

                str.WriteLine( "[Node] // Node " + i.ToString() + " " + n.comment );
                n.WriteToStream( str );
                str.WriteLine();
            }

            //str.WriteLine( "[LookupData]\nVersion=1.101\nBin=0\nChecksum=0" );
        }

        /*public void FromFile( string FilePath ) {
            rFDataFile m_BaseData = new rFDataFile( FilePath );

            int numNodes = 0;

            foreach ( rFParam a in m_BaseData.m_params ) {
                if ( a.section == "[QuasiStaticAnalysis]" && a.name == "NumNodes" ) {
                    numNodes = a.GetInt();
                    break;
                }
            }

            nodes = new Node[numNodes];
            int i = -1;
            foreach ( rFParam a in m_BaseData.m_params ) {
                if ( a.section == "[Node]" ) {
                    if ( a.name == "[Geometry]" ) {
                        i++;
                        nodes[i].x = a.GetDouble( 0 );
                        nodes[i].y = a.GetDouble( 1 );
                        nodes[i].d = a.GetDouble( 2 );
                    }

                    if(
                }
            }
        }*/
    }
}
